package server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import model.Conversacion;
import model.Usuario;
import protocol.MensajeComando;
import server.exception.ClientAcceptException;
import server.exception.ServerConnectException;

import comandos.Alerta;
import comandos.InterpreteComando;
import comandos.MensajeChat;

public class Servidor 
{
	private ServerSocket serverSocket;
	private boolean active = false;
	private int puerto;
	private static HashMap<Integer,Conversacion> conversacionesActivas = new HashMap<Integer,Conversacion>();
	private static HashMap<Integer,ClienteHilo> usuariosConectados = new HashMap<Integer,ClienteHilo>();
		
	public static synchronized void enviarInvitacionAConversar(Conversacion conversacion, Usuario invitador, Usuario invitado) {
		// le envio el mensaje
		ClienteHilo invitadoHilo = usuariosConectados.get(invitado.getId());
		try
		{
			invitadoHilo.getOutput().writeObject(new MensajeComando("invitar_a_conversar",0));
			invitadoHilo.getOutput().writeObject(invitador);
			invitadoHilo.getOutput().writeObject(conversacion);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
	}
	
	public static synchronized void aceptarInvitacionAConversar(Conversacion conversacion, Usuario invitador, Usuario invitado) {
		// le envio el mensaje
		ClienteHilo invitadorHilo = usuariosConectados.get(invitador.getId());
		try
		{
			invitadorHilo.getOutput().writeObject(new MensajeComando("aceptar_invitacion_a_conversar",0));
			invitadorHilo.getOutput().writeObject(invitado);
			invitadorHilo.getOutput().writeObject(conversacion);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		
	}
	
	public static synchronized void addConversacion(Conversacion c) 
	{
		if (!conversacionesActivas.containsKey(c.getId())){
			conversacionesActivas.put(c.getId(), c);
		}else{
			Conversacion convers = conversacionesActivas.get(c.getId());
			convers.setMensajes(c.getMensajes());
			convers.setUsuarios(c.getUsuarios());
		}
	}
	
	public static synchronized void removeConversacion(Conversacion c) 
	{
		if (conversacionesActivas.containsKey(c.getId()))
		{
			conversacionesActivas.remove(c.getId());
		}
		
	}
	
	public static synchronized void addUsuarioConectado (Usuario u, ClienteHilo c)
	{
		if (!usuariosConectados.containsKey(u.getId()))
		{
			usuariosConectados.put(u.getId(), c);
			//Actualizo a todos los hilos con la nueva lista de usuarios
			//enviarAmigosConectados();
		}
	}
	
	public static synchronized void removeUsuarioConectado (Usuario u)
	{
		if (usuariosConectados.containsKey(u.getId()))
		{
			usuariosConectados.remove(u.getId());
			//Actualizo a todos los hilos con la nueva lista de usuarios
			//enviarAmigosConectados();
		}
	}
	
	public static synchronized void matarHiloCliente(Usuario u) throws IOException
	{
		ClienteHilo clienteHiloServidor = usuariosConectados.get(u.getId());
		clienteHiloServidor.setActive(false);
		clienteHiloServidor.getClientSocket().close();
		removeUsuarioConectado(u);
		System.out.println("Login - El usuario <" + clienteHiloServidor.getUsuario().getNick() + "> ha sido desconectado");
	}
	
	
	public static synchronized ArrayList<Usuario> getUsuariosConectados()
	{
		ArrayList<Usuario> usuariosConectadosAL = new ArrayList<Usuario>();
		Iterator<ClienteHilo> uc = usuariosConectados.values().iterator();
		while (uc.hasNext())
		{
			Usuario user = uc.next().getUsuario();
			usuariosConectadosAL.add(user);
		}
		return usuariosConectadosAL;
	}
	
	public static synchronized boolean isConectado (Usuario u)
	{
		return usuariosConectados.containsKey(u.getId());
	}
	
	public static synchronized void enviarMensaje(Usuario usuario, String mensaje) {
		// le envio el mensaje
		ClienteHilo ch = usuariosConectados.get(usuario.getId());
		Alerta comando = (Alerta) InterpreteComando.obtenerComando("alerta");
		comando.setMensaje(mensaje);
		comando.ejecutar(ch);
	}
	
	public static void enviarMensajeAConectados(String mensaje) {
		ArrayList<Usuario> usuarios = Servidor.getUsuariosConectados();
		for(Usuario usuario : usuarios)
			Servidor.enviarMensaje(usuario, mensaje);
	}
	
	public void start(int puerto) throws ServerConnectException, ClientAcceptException
	{
		try 
		{
			this.puerto=puerto;
			this.serverSocket = new ServerSocket(puerto);//Configuration.PUERTO_SERVICIO);
			this.active = true;
		} 
		catch (Exception e) 
		{
			throw new ServerConnectException("No pudo comenzarse con la ejecucion del servicio, verifique que el puerto en escucha no se encuentre ocupado");
		}
		
		try 
		{
			while (true) 
			{
				Socket clienteSocket = this.serverSocket.accept();
				Thread clienteHilo = new Thread (new ClienteHilo(clienteSocket));
				clienteHilo.start();
			}
		}
		catch (Exception e) 
		{
			throw new ClientAcceptException("No pudo establecerse la conexion con el cliente");
		}
	}
	
	public void stop()
	{
		try {
			this.serverSocket.close();
			ArrayList<Usuario> usuarios = Servidor.getUsuariosConectados();
			setActive(false);
			for(Usuario usuario : usuarios)
				Servidor.matarHiloCliente(usuario);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}
	
	public static ClienteHilo getClienteHilo(Usuario usuario) {
		return Servidor.usuariosConectados.get(usuario.getId());
	}
	
	public int getPuerto() {
		return puerto;
	}
	
	public static void main (String args[])
	{
		
		
		
		Servidor s = new Servidor ();
		try
		{
			s.start(Configuration.PUERTO_SERVICIO);
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
		
		
		
	}

	public synchronized static void envioMensajeChat(Usuario origen, Usuario destino, String mensaje, Integer conversacionId) {
		// le envio el mensaje
		ClienteHilo ch = usuariosConectados.get(destino.getId());
		if(ch != null){
			MensajeChat comandoMensaje = (MensajeChat) InterpreteComando.obtenerComando("enviar_mensaje_chat");
			
			comandoMensaje.setMensaje(mensaje);	
			comandoMensaje.setConversacionId(conversacionId);	
			comandoMensaje.setUsuarioOrigen(origen);
			
			comandoMensaje.ejecutar(ch);
		}
	}
	
	public synchronized static boolean enviarMensajeConversacion(Usuario usuario, String mensaje, Integer conversacionId) {
		Conversacion conversacion = conversacionesActivas.get(conversacionId);
		ArrayList<Usuario> listaParticipantes = new ArrayList<Usuario>();
		listaParticipantes.addAll(conversacion.getUsuarios());		

		//Logs..
		StringBuffer sb = new StringBuffer("Conversaciones - Enviando mensaje de <" + usuario + "> hacia: [");
		for(Usuario u:listaParticipantes){
			sb.append("<" + u + "> ");
		}
		sb.append("] mensaje: " + mensaje);		
		System.out.println(sb.toString());
		
		try{
			//Ahora envio los mensajes
			for(Usuario u:listaParticipantes){
				if(u.getId() != usuario.getId())
					Servidor.envioMensajeChat(usuario, u, mensaje, conversacionId);
			}
		}catch(Exception e){
			System.out.println(e.getMessage());
			return false;
		}
		return true;		
	}

	public synchronized static void invitarAChatear(Usuario u, Usuario usuarioCreador, Conversacion conversacion) throws IOException {
		ClienteHilo cliente = usuariosConectados.get(u.getId());
		
		//Le envio a los usuarios el inicio de la conversacion
		cliente.getOutput().writeObject(new MensajeComando("me_agregan_conversacion",0));
		cliente.getOutput().flush();
		
		//Envio el id de conversacion y los participantes
		cliente.getOutput().writeObject(conversacion);
		cliente.getOutput().flush();
		
		//Envio el usuario que inicia la convers
		cliente.getOutput().writeObject(usuarioCreador);
		cliente.getOutput().flush();
		
	}

	
	

}
